<?php

declare( strict_types=1 );

namespace simpleHandle\Component\UtilPdo;

use simpleHandle\Exception\UtilException;
use Swoole\Database\PDOConfig;
use Swoole\Database\PDOPool;
use Throwable;
use Exception;

class Connection
{
	/**
	 * @var PDOPool
	 */
	protected PDOPool $pools;
	private static    $instance;
	protected array   $config = [
		'host'       => 'localhost',
		'port'       => 3306,
		'database'   => 'test',
		'username'   => 'root',
		'password'   => 'root',
		'charset'    => 'utf8mb4',
		'unixSocket' => null,
		'options'    => [],
		'size'       => 64,
	];

	/**
	 * @throws UtilException
	 */
	private function __construct( array $config ) {
		if ( empty( $this->pools ) ) {
			$this->config = array_replace_recursive( $this->config, $config );
			try {
				$this->pools = new PDOPool(
					( new PDOConfig() )
						->withHost( $this->config['host'] )
						->withPort( $this->config['port'] )
						->withUnixSocket( $this->config['unixSocket'] )
						->withDbName( $this->config['database'] )
						->withCharset( $this->config['charset'] )
						->withUsername( $this->config['username'] )
						->withPassword( $this->config['password'] )
						->withOptions( $this->config['options'] ),
					$this->config['size']
				);
			} catch ( Throwable $th ) {
				throw new UtilException( $th->getMessage(), UtilException::EasyPdo_ERROR_CODE );
			}
		}
	}

	/**
	 * @throws UtilException
	 */
	public static function getInstance( $config = null, $poolName = 'default' ) {
		try {
			if ( empty( self::$instance[ $poolName ] ) ) {
				if ( empty( $config ) ) {
					throw new Exception( 'pdo config empty' );
				}
				if ( empty( $config['size'] ) ) {
					throw new Exception( 'the size of database connection pools cannot be empty' );
				}
				self::$instance[ $poolName ] = new static( $config );
			}

			return self::$instance[ $poolName ];
		} catch ( Throwable $th ) {
			throw new UtilException( $th->getMessage(), UtilException::EasyPdo_ERROR_CODE );
		}
	}

	/**
	 * @throws UtilException
	 */
	public function getConnection() {
		try {
			$connection = $this->pools->get();
			if ( empty( $connection ) ) {
				throw new Exception( "pool is empty" );
			}

			return $connection;
		} catch ( Throwable $th ) {
			throw new UtilException( $th->getMessage(), UtilException::EasyPdo_ERROR_CODE );
		}
	}

	/**
	 * @throws UtilException
	 */
	public function close( $connection = null ) {
		try {
			$this->pools->put( $connection );
		} catch ( Throwable $th ) {
			throw new UtilException( $th->getMessage(), UtilException::EasyPdo_ERROR_CODE );
		}
	}
}